package fi.pakkanen.yarpg.tools;

public class Range {

	private final int from;
	private final int to;
	private final boolean includingRange;
	private final int hashCode;

	/**
	 * Create a range from a lower bound to an upper bound excluding the upper
	 * bound.
	 * 
	 * @param from
	 *            the lower bound
	 * @param to
	 *            the excluded upper bound
	 */
	public Range(int from, int to) {
		this(from, to, false);
	}

	/**
	 * Create a range from a lower bound to an upper bound.
	 * 
	 * @param from
	 *            the lower bound
	 * @param to
	 *            the upper bound
	 * @param includingRange
	 *            should the upper bound be included
	 */
	public Range(int from, int to, boolean includingRange) {

		if (from > to) {
			throw new IllegalStateException("Lower bound " + from
					+ " must be equal or greater than the upper bound" + to);
		}

		this.from = from;
		this.to = to;
		this.includingRange = includingRange;
		this.hashCode = calculateHashCode();
	}

	/**
	 * Get the lower bound of this range.
	 * 
	 * @return the lower bound
	 */
	public int getLowerBound() {
		return from;
	}

	/**
	 * Get the upper bound of this range.
	 * 
	 * @return the upper bound
	 */
	public int getUpperBound() {
		return to;
	}

	/**
	 * Check whether the upper bound is included and a valid value in this
	 * range.
	 * 
	 * @return true when the upper bound is included, false otherwise
	 */
	public boolean isUpperBoundIncluded() {
		return includingRange;
	}

	/**
	 * Check whether the given value is in range.
	 * 
	 * @param value
	 * @return true when it is in range, false otherwise
	 */
	public boolean isInRange(int value) {
		return value >= from && (includingRange ? value <= to : value < to);
	}

	/**
	 * Calculate the hash code of this range.
	 * 
	 * @return the hash code
	 */
	private int calculateHashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + from;
		result = prime * result + (includingRange ? 1231 : 1237);
		result = prime * result + to;
		return result;
	}

	@Override
	public String toString() {
		return "Range[" + from + (includingRange ? "..." : "..") + to + "]";
	}

	@Override
	public int hashCode() {
		return hashCode;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null || getClass() != obj.getClass()) {
			return false;
		}
		Range other = (Range) obj;
		if (from != other.from) {
			return false;
		}
		if (includingRange != other.includingRange) {
			return false;
		}
		if (to != other.to) {
			return false;
		}
		return true;
	}
}
